package org.qtmo.emu.nes;

/**
 * ...
 * @author Sang Hua Cai
 */

class PPU
{

	public function new(nes:NES)
	{
		this.nes = nes;
		
		PPU6_Value = 0x0000;
		PPU6_Temp = 0x00;
		PPU7_Temp = 0xFF;
		PPU56Toggle = false;
	}	
	
	public function Reset():Void
	{
		//bExtLatch = FALSE;
		//bChrLatch = FALSE;
		//bExtNameTable = FALSE;
		//bExtMono = FALSE;

		SetPPUREG(0, 0);
		SetPPUREG(1, 0);
		PPU56Toggle = false;
		PPU7_Temp = 0xFF;
		PPU6_Value = 0;
		PPU6_Temp = 0;
		PPU6_X = 0;
		PPU6_Y = 0;
		PPU6_Shift = 0;

		var total:UInt = SCREEN_WIDTH * SCREEN_HEIGHT;
		for (i in 0...total)
		{
			MMU.Write(MMU.SCREEN_OFFSET + i, 0x3F);
		}
		for (i in 0...SCREEN_HEIGHT)
		{
			MMU.Write(MMU.SCANLINE_COLOR_OFFSET + i, 0);
		}
	}
	
	public function Read(addr:UInt):UInt
	{
		var data:UInt = 0x00;

		switch( addr )
		{
			// Read/Write Register
			case	0x2002: // PPU Status Register(R)
			data = GetPPUREG(2);
			PPU56Toggle = false;
			SetPPUREG(2, GetPPUREG(2) & ~PPU_VBLANK_FLAG);
			
			case	0x2004: // SPR_RAM I/O Register(RW)
			data = GetSPRAM(GetPPUREG(3));
			SetPPUREG(3, GetPPUREG(3) + 1);
			
			case	0x2007: // VRAM I/O Register(RW)
			var	addr:UInt = PPU6_Value & 0x3FFF;
			data = PPU7_Temp;
			if ( GetPPUREG(0) & PPU_INC32_BIT > 0)
			{
				PPU6_Value += 32;
			}
			else
			{
				PPU6_Value ++;
			}
			if ( addr >= 0x3000 )
			{
				if ( addr >= 0x3F00 )
				{
					return  MMU.Read(addr + MMU.PPU_BANK_OFFSET);
				}
				addr &= 0xEFFF;
			}
			PPU7_Temp = MMU.Read((addr & 0x03FF) + MMU.PPU_BANK_OFFSET);
		}

		return	data;
	}

	public function Write( addr:UInt, data:UInt )
	{
		switch( addr ) 
		{
			// Write Register
			case	0x2000: // PPU Control Register #1(W)
			// NameTable select
			// t:0000110000000000=d:00000011
			PPU6_Temp = (PPU6_Temp & 0xF3FF)|((data & 0x03)<<10);
			if ( (data & 0x80 > 0) && !(GetPPUREG(0) & 0x80 > 0) && (GetPPUREG(2) & 0x80 > 0) )
			{
				nes.cpu.NMI();
			}
			SetPPUREG(0, data);

			case	0x2001: // PPU Control Register #2(W)
			SetPPUREG(1, data);
			
			case	0x2003: // SPR-RAM Address Register(W)
			SetPPUREG(3, data);
			
			case	0x2004: // SPR_RAM I/O Register(RW)
			SetSPRAM(GetPPUREG(3), data);
			SetPPUREG(3, GetPPUREG(3) + 1);

			case	0x2005: // PPU Scroll Register(W2)
			if ( !PPU56Toggle )
			{
				// First write
				// tile X t:0000000000011111=d:11111000
				PPU6_Temp = (PPU6_Temp & 0xFFE0)|(data>>3);
				// scroll offset X x=d:00000111
				PPU6_X = data & 0x07;
			}
			else
			{
				// Second write
				// tile Y t:0000001111100000=d:11111000
				PPU6_Temp = (PPU6_Temp & 0xFC1F)|((data & 0xF8)<<2);
				// scroll offset Y t:0111000000000000=d:00000111
				PPU6_Temp = (PPU6_Temp & 0x8FFF)|((data & 0x07)<<12);
			}
			PPU56Toggle = !PPU56Toggle;

			case	0x2006: // VRAM Address Register(W2)
			if ( !PPU56Toggle )
			{
				// First write
				// t:0011111100000000=d:00111111
				// t:1100000000000000=0
				PPU6_Temp = (PPU6_Temp & 0x00FF)|((data & 0x3F)<<8);
			}
			else
			{
				// Second write
				// t:0000000011111111=d:11111111
				PPU6_Temp = (PPU6_Temp & 0xFF00)|data;
				// v=t
				PPU6_Value = PPU6_Temp;
				//T)_DO
				//nes->mapper->PPU_Latch( loopy_v );
			}
			PPU56Toggle = !PPU56Toggle;

			case	0x2007: // VRAM I/O Register(RW)
			if ( GetPPUREG(0) & PPU_INC32_BIT > 0 )
			{
				PPU6_Value+=32;
			}
			else
			{
				PPU6_Value++;
			}
			if ( PPU6_Value >= 0x3000 )
			{
				if ( PPU6_Value >= 0x3F00 )
				{
					data &= 0x3F;
					if ( !(PPU6_Value & 0x000F > 0) )
					{
						MMU.Write(MMU.BGPAL_OFFSET, data);
						MMU.Write(MMU.SPPAL_OFFSET, data);
					}
					else if ( !(PPU6_Value & 0x0010 > 0) )
					{
						MMU.Write(MMU.BGPAL_OFFSET + (PPU6_Value & 0x000F), data);
					}
					else
					{
						MMU.Write(MMU.SPPAL_OFFSET + (PPU6_Value & 0x000F), data);
					}
					MMU.Write(MMU.BGPAL_OFFSET + 0x04, MMU.Read(MMU.BGPAL_OFFSET));
					MMU.Write(MMU.BGPAL_OFFSET + 0x08, MMU.Read(MMU.BGPAL_OFFSET));
					MMU.Write(MMU.BGPAL_OFFSET + 0x0C, MMU.Read(MMU.BGPAL_OFFSET));
					MMU.Write(MMU.SPPAL_OFFSET + 0x00, MMU.Read(MMU.BGPAL_OFFSET));
					MMU.Write(MMU.SPPAL_OFFSET + 0x04, MMU.Read(MMU.BGPAL_OFFSET));
					MMU.Write(MMU.SPPAL_OFFSET + 0x05, MMU.Read(MMU.BGPAL_OFFSET));
					MMU.Write(MMU.SPPAL_OFFSET + 0x0C, MMU.Read(MMU.BGPAL_OFFSET));
					return;
				}
			}
			//TO-DO
			/*
			if( PPU_MEM_TYPE[PPU6_Value>>10] != BANKTYPE_VROM ) {
			PPU_MEM_BANK[PPU6_Value>>10][PPU6_Value&0x03FF] = data;
			}*/
		}
	}
	
	public function FrameStart():Void
	{
		
	}
	
	public function SetRenderScanline( scanline:Int ):Void
	{
		
	}
	
	public function VBlankStart():Void
	{
		
	}
	
	public function VBlankEnd():Void
	{
		
	}
	
	public function DummyScanline():Void
	{
		
	}
	
	public function ScanlineNext():Void
	{
	
	}
	
	public function ScanlineStart():Void
	{
		
	}
	
	public function Scanline( scanline:Int ):Void
	{
		/*
		 * 
		
		 bMax:Bool = true;
		 bLeftClip:Bool = true;
		 
	BYTE	BGwrite[33+1];
	//BYTE	BGmono[33+1];

		ZEROMEMORY( BGwrite, sizeof(BGwrite) );
		//ZEROMEMORY( BGmono, sizeof(BGmono) );

		// Linecolor mode
		MMU.Write(scanline + MMU.SCANLINE_COLOR_OFFSET, ((GetPPUREG(1) & PPU_BGCOLOR_BIT) >> 5) | ((GetPPUREG(1) & PPU_COLORMODE_BIT) << 7));

		// Render BG
		if ( !(PPUREG[1] & PPU_BGDISP_BIT) ) 
		{
			for (i in 0...SCREEN_WIDTH)
			{
				MMU.Write(i + MMU.SCREEN_OFFSET, MMU.Read(MMU.BGPAL_OFFSET));
			}
		}
		else 
		{
			if ( !bExtLatch ) 
			{
				// Without Extension Latch
				LPBYTE	pScn = lpScanline+(8-loopy_shift);
				LPBYTE	pBGw = BGwrite;

				INT	tileofs = (PPUREG[0]&PPU_BGTBL_BIT)<<8;
				INT	ntbladr = 0x2000+(loopy_v&0x0FFF);
				INT	attradr = 0x23C0+(loopy_v&0x0C00)+((loopy_v&0x0380)>>4);
				INT	ntbl_x  = ntbladr&0x001F;
				INT	attrsft = (ntbladr&0x0040)>>4;
				LPBYTE	pNTBL = PPU_MEM_BANK[ntbladr>>10];

				INT	tileadr;
				INT	cache_tile = 0xFFFF0000;
				BYTE	cache_attr = 0xFF;

				BYTE	chr_h, chr_l, attr;

				attradr &= 0x3FF;

				for( INT i = 0; i < 33; i++ ) {
					tileadr = tileofs+pNTBL[ntbladr&0x03FF]*0x10+loopy_y;
					attr = ((pNTBL[attradr+(ntbl_x>>2)]>>((ntbl_x&2)+attrsft))&3)<<2;

					if( cache_tile == tileadr && cache_attr == attr ) {
						*(LPDWORD)(pScn+0) = *(LPDWORD)(pScn-8);
						*(LPDWORD)(pScn+4) = *(LPDWORD)(pScn-4);
						*(pBGw+0) = *(pBGw-1);
					} else {
						cache_tile = tileadr;
						cache_attr = attr;
						chr_l = PPU_MEM_BANK[tileadr>>10][ tileadr&0x03FF   ];
						chr_h = PPU_MEM_BANK[tileadr>>10][(tileadr&0x03FF)+8];
						*pBGw = chr_h|chr_l;

						LPBYTE	pBGPAL = &BGPAL[attr];
						{
						register INT	c1 = ((chr_l>>1)&0x55)|(chr_h&0xAA);
						register INT	c2 = (chr_l&0x55)|((chr_h<<1)&0xAA);
						pScn[0] = pBGPAL[(c1>>6)];
						pScn[4] = pBGPAL[(c1>>2)&3];
						pScn[1] = pBGPAL[(c2>>6)];
						pScn[5] = pBGPAL[(c2>>2)&3];
						pScn[2] = pBGPAL[(c1>>4)&3];
						pScn[6] = pBGPAL[c1&3];
						pScn[3] = pBGPAL[(c2>>4)&3];
						pScn[7] = pBGPAL[c2&3];
						}
					}
					pScn+=8;
					pBGw++;

					// Character latch(For MMC2/MMC4)
					if( bChrLatch ) {
						nes->mapper->PPU_ChrLatch( tileadr );
					}

					if( ++ntbl_x == 32 ) {
						ntbl_x = 0;
						ntbladr ^= 0x41F;
						attradr = 0x03C0+((ntbladr&0x0380)>>4);
						pNTBL = PPU_MEM_BANK[ntbladr>>10];
					} else {
						ntbladr++;
					}
				}
			} 
			else 
			{
				// With Extension Latch(For MMC5)
				LPBYTE	pScn = lpScanline+(8-loopy_shift);
				LPBYTE	pBGw = BGwrite;

				INT	ntbladr = 0x2000+(loopy_v&0x0FFF);
				INT	ntbl_x  = ntbladr & 0x1F;

				INT	cache_tile = 0xFFFF0000;
				BYTE	cache_attr = 0xFF;

				BYTE	chr_h, chr_l, attr, exattr;

				for( INT i = 0; i < 33; i++ ) {
					nes->mapper->PPU_ExtLatchX( i );
					nes->mapper->PPU_ExtLatch( ntbladr, chr_l, chr_h, exattr );
					attr = exattr&0x0C;

					if( cache_tile != (((INT)chr_h<<8)+(INT)chr_l) || cache_attr != attr ) {
						cache_tile = (((INT)chr_h<<8)+(INT)chr_l);
						cache_attr = attr;
						*pBGw = chr_h|chr_l;

						LPBYTE	pBGPAL = &BGPAL[attr];
						{
						register INT	c1 = ((chr_l>>1)&0x55)|(chr_h&0xAA);
						register INT	c2 = (chr_l&0x55)|((chr_h<<1)&0xAA);
						pScn[0] = pBGPAL[(c1>>6)];
						pScn[4] = pBGPAL[(c1>>2)&3];
						pScn[1] = pBGPAL[(c2>>6)];
						pScn[5] = pBGPAL[(c2>>2)&3];
						pScn[2] = pBGPAL[(c1>>4)&3];
						pScn[6] = pBGPAL[c1&3];
						pScn[3] = pBGPAL[(c2>>4)&3];
						pScn[7] = pBGPAL[c2&3];
						}
					} else {
						*(DWORD*)(pScn+0) = *(DWORD*)(pScn-8);
						*(DWORD*)(pScn+4) = *(DWORD*)(pScn-4);
						*(pBGw+0) = *(pBGw-1);
					}
					pScn+=8;
					pBGw++;

					if( ++ntbl_x == 32 ) {
						ntbl_x = 0;
						ntbladr ^= 0x41F;
					} else {
						ntbladr++;
					}
				}
			}

			if( !(PPUREG[1]&PPU_BGCLIP_BIT) && bLeftClip ) {
				LPBYTE	pScn = lpScanline+8;
				for( INT i = 0; i < 8; i++ ) {
					pScn[i] = BGPAL[0];
				}
			}
		}

		// Render sprites
		PPUREG[2] &= ~PPU_SPMAX_FLAG;

		if ( scanline > 239 )
		{
			return;
		}
		
		if ( !(PPUREG[1] & PPU_SPDISP_BIT) ) 
		{
			return;
		}

		BYTE	SPwrite[33+1];
		INT	spmax;
		INT	spraddr, sp_y, sp_h;
		BYTE	chr_h, chr_l;
		LPSPRITE sp;

		LPBYTE	pBGw = BGwrite;
		LPBYTE	pSPw = SPwrite;
		LPBYTE	pBit2Rev = Bit2Rev;

		ZEROMEMORY( SPwrite, sizeof(SPwrite) );

		spmax = 0;
		sp = (LPSPRITE)SPRAM;
		sp_h = (PPUREG[0]&PPU_SP16_BIT)?15:7;

		// Left clip
		if( !(PPUREG[1]&PPU_SPCLIP_BIT) && bLeftClip ) {
			SPwrite[0] = 0xFF;
		}

		for( INT i = 0; i < 64; i++, sp++ ) {
			sp_y = scanline - (sp->y+1);
			// 僗僉儍儞儔僀儞撪偵SPRITE偑懚嵼偡傞偐傪僠僃僢僋
			if( sp_y != (sp_y & sp_h) )
				continue;

			if( !(PPUREG[0]&PPU_SP16_BIT) ) {
			// 8x8 Sprite
				spraddr = (((INT)PPUREG[0]&PPU_SPTBL_BIT)<<9)+((INT)sp->tile<<4);
				if( !(sp->attr&SP_VMIRROR_BIT) )
					spraddr += sp_y;
				else
					spraddr += 7-sp_y;
			} else {
			// 8x16 Sprite
				spraddr = (((INT)sp->tile&1)<<12)+(((INT)sp->tile&0xFE)<<4);
				if( !(sp->attr&SP_VMIRROR_BIT) )
					spraddr += ((sp_y&8)<<1)+(sp_y&7);
				else
					spraddr += ((~sp_y&8)<<1)+(7-(sp_y&7));
			}
			// Character pattern
			chr_l = PPU_MEM_BANK[spraddr>>10][ spraddr&0x3FF   ];
			chr_h = PPU_MEM_BANK[spraddr>>10][(spraddr&0x3FF)+8];

			// Character latch(For MMC2/MMC4)
			if( bChrLatch ) {
				nes->mapper->PPU_ChrLatch( spraddr );
			}

			// pattern mask
			if( sp->attr&SP_HMIRROR_BIT ) {
				chr_l = pBit2Rev[chr_l];
				chr_h = pBit2Rev[chr_h];
			}
			BYTE	SPpat = chr_l|chr_h;

			// Sprite hitcheck
			if( i == 0 && !(PPUREG[2]&PPU_SPHIT_FLAG) ) {
				INT	BGpos = ((sp->x&0xF8)+((loopy_shift+(sp->x&7))&8))>>3;
				INT	BGsft = 8-((loopy_shift+sp->x)&7);
				BYTE	BGmsk = (((WORD)pBGw[BGpos+0]<<8)|(WORD)pBGw[BGpos+1])>>BGsft;

				if( SPpat & BGmsk ) {
					PPUREG[2] |= PPU_SPHIT_FLAG;
				}
			}

			// Sprite mask
			INT	SPpos = sp->x/8;
			INT	SPsft = 8-(sp->x&7);
			BYTE	SPmsk = (((WORD)pSPw[SPpos+0]<<8)|(WORD)pSPw[SPpos+1])>>SPsft;
			WORD	SPwrt = (WORD)SPpat<<SPsft;
			pSPw[SPpos+0] |= SPwrt >> 8;
			pSPw[SPpos+1] |= SPwrt & 0xFF;
			SPpat &= ~SPmsk;

			if( sp->attr&SP_PRIORITY_BIT ) {
			// BG > SP priority
				INT	BGpos = ((sp->x&0xF8)+((loopy_shift+(sp->x&7))&8))>>3;
				INT	BGsft = 8-((loopy_shift+sp->x)&7);
				BYTE	BGmsk = (((WORD)pBGw[BGpos+0]<<8)|(WORD)pBGw[BGpos+1])>>BGsft;

				SPpat &= ~BGmsk;
			}

			// Attribute
			LPBYTE	pSPPAL = &SPPAL[(sp->attr&SP_COLOR_BIT)<<2];
			// Ptr
			LPBYTE	pScn   = lpScanline+sp->x+8;

			//if( !bExtMono ) 
			{
				register INT	c1 = ((chr_l>>1)&0x55)|(chr_h&0xAA);
				register INT	c2 = (chr_l&0x55)|((chr_h<<1)&0xAA);
				if( SPpat&0x80 ) pScn[0] = pSPPAL[(c1>>6)];
				if( SPpat&0x08 ) pScn[4] = pSPPAL[(c1>>2)&3];
				if( SPpat&0x40 ) pScn[1] = pSPPAL[(c2>>6)];
				if( SPpat&0x04 ) pScn[5] = pSPPAL[(c2>>2)&3];
				if( SPpat&0x20 ) pScn[2] = pSPPAL[(c1>>4)&3];
				if( SPpat&0x02 ) pScn[6] = pSPPAL[c1&3];
				if( SPpat&0x10 ) pScn[3] = pSPPAL[(c2>>4)&3];
				if( SPpat&0x01 ) pScn[7] = pSPPAL[c2&3];
			} 
			else {
			// Monocrome effect (for Final Fantasy)
				//BYTE	mono = BGmono[((sp->x&0xF8)+((loopy_shift+(sp->x&7))&8))>>3];

				//register INT	c1 = ((chr_l>>1)&0x55)|(chr_h&0xAA);
				//register INT	c2 = (chr_l&0x55)|((chr_h<<1)&0xAA);
				//if( SPpat&0x80 ) pScn[0] = pSPPAL[c1>>6]	|mono;
				//if( SPpat&0x08 ) pScn[4] = pSPPAL[(c1>>2)&3]	|mono;
				//if( SPpat&0x40 ) pScn[1] = pSPPAL[c2>>6]	|mono;
				//if( SPpat&0x04 ) pScn[5] = pSPPAL[(c2>>2)&3]	|mono;
				//if( SPpat&0x20 ) pScn[2] = pSPPAL[(c1>>4)&3]	|mono;
				//if( SPpat&0x02 ) pScn[6] = pSPPAL[c1&3]		|mono;
				//if( SPpat&0x10 ) pScn[3] = pSPPAL[(c2>>4)&3]	|mono;
				//if( SPpat&0x01 ) pScn[7] = pSPPAL[c2&3]		|mono;
			}

			if( ++spmax > 8-1 ) {
				if( !bMax )
					break;
			}
		}
		if( spmax > 8-1 ) {
			PPUREG[2] |= PPU_SPMAX_FLAG;
		}*/
	}
	
	
	/*

// 僗僾儔僀僩侽偑僸僢僩偡傞偐傕抦傟側偄儔僀儞丠
BOOL	PPU::IsSprite0( INT scanline )
{
	// 僗僾儔僀僩orBG旕昞帵偼僉儍儞僙儖(僸僢僩偟側偄)
	if( (PPUREG[1]&(PPU_SPDISP_BIT|PPU_BGDISP_BIT)) != (PPU_SPDISP_BIT|PPU_BGDISP_BIT) )
		return	FALSE;

	// 婛偵僸僢僩偟偰偄偨傜僉儍儞僙儖
	if( PPUREG[2]&PPU_SPHIT_FLAG )
		return	FALSE;

	if( !(PPUREG[0]&PPU_SP16_BIT) ) {
	// 8x8
		if( (scanline < (INT)SPRAM[0]+1) || (scanline > ((INT)SPRAM[0]+7+1)) )
			return	FALSE;
	} else {
	// 8x16
		if( (scanline < (INT)SPRAM[0]+1) || (scanline > ((INT)SPRAM[0]+15+1)) )
			return	FALSE;
	}

	return	TRUE;
}

void	PPU::DummyScanline( INT scanline )
{
INT	i;
INT	spmax;
INT	sp_h;
LPSPRITE sp;

	PPUREG[2] &= ~PPU_SPMAX_FLAG;

	// 僗僾儔僀僩旕昞帵偼僉儍儞僙儖
	if( !(PPUREG[1]&PPU_SPDISP_BIT) )
		return;

	// 昞帵婜娫奜偱偁傟偽僉儍儞僙儖
	if( scanline < 0 || scanline > 239 )
		return;

	sp = (LPSPRITE)SPRAM;
	sp_h = (PPUREG[0]&PPU_SP16_BIT)?15:7;

	spmax = 0;
	// Sprite Max check
	for( i = 0; i < 64; i++, sp++ ) {
		// 僗僉儍儞儔僀儞撪偵SPRITE偑懚嵼偡傞偐傪僠僃僢僋
		if( (scanline < (INT)sp->y+1) || (scanline > ((INT)sp->y+sp_h+1)) ) {
			continue;
		}

		if( ++spmax > 8-1 ) {
			PPUREG[2] |= PPU_SPMAX_FLAG;
			break;
		}
	}
}*/

	public static inline function GetPPUREG(addr:UInt):UInt
	{
		return MMU.Read(addr + MMU.PPU_REG_OFFSET);
	}
	
	public static inline function SetPPUREG(addr:UInt, value):Void
	{
		MMU.Write(addr + MMU.PPU_REG_OFFSET, value);
	}
	
	private static inline function GetSPRAM(addr:UInt):UInt
	{
		return MMU.Read(addr + MMU.SPRAM_OFFSET);
	}
	
	private static inline function SetSPRAM(addr:UInt, value):Void
	{
		MMU.Write(addr + MMU.SPRAM_OFFSET, value);
	}
	
	private var nes:NES;
	
	private var PPU6_Value:UInt;
	private var PPU6_Temp:UInt;
	private var PPU6_Shift:UInt;
	private var PPU6_X:UInt;
	private var PPU6_Y:UInt;
	private var PPU56Toggle:Bool;	
	private var PPU7_Temp:UInt;
	
	public static inline var	SCREEN_WIDTH:UInt		= 256;
	public static inline var	SCREEN_HEIGHT:UInt		= 240;

	// PPU Control Register #1	PPU #0
	public static inline var	PPU_VBLANK_BIT:UInt 	= 0x80;
	public static inline var	PPU_SPHIT_BIT:UInt 		= 0x40;
	public static inline var	PPU_SP16_BIT:UInt 		= 0x20;
	public static inline var	PPU_BGTBL_BIT:UInt 		= 0x10;
	public static inline var	PPU_SPTBL_BIT:UInt 		= 0x08;
	public static inline var	PPU_INC32_BIT:UInt 		= 0x04;
	public static inline var	PPU_NAMETBL_BIT:UInt 	= 0x03;

	// PPU Control Register #2	PPU #1
	public static inline var	PPU_BGCOLOR_BIT:UInt 	= 0xE0;
	public static inline var	PPU_SPDISP_BIT:UInt		= 0x10;
	public static inline var	PPU_BGDISP_BIT:UInt 	= 0x08;
	public static inline var	PPU_SPCLIP_BIT:UInt 	= 0x04;
	public static inline var	PPU_BGCLIP_BIT:UInt 	= 0x02;
	public static inline var	PPU_COLORMODE_BIT:UInt 	= 0x01;

	// PPU Status Register		PPU #2
	public static inline var	PPU_VBLANK_FLAG:UInt 	= 0x80;
	public static inline var	PPU_SPHIT_FLAG:UInt 	= 0x40;
	public static inline var	PPU_SPMAX_FLAG:UInt 	= 0x20;
	public static inline var	PPU_WENABLE_FLAG:UInt 	= 0x10;

	// SPRITE Attribute
	public static inline var	SP_VMIRROR_BIT:UInt 	= 0x80;
	public static inline var	SP_HMIRROR_BIT:UInt 	= 0x40;
	public static inline var	SP_PRIORITY_BIT:UInt 	= 0x20;
	public static inline var	SP_COLOR_BIT:UInt 		= 0x03;
}

